package manager.utility;

import static manager.utility.StaticFields.CONTENT_ELEM;
import static manager.utility.StaticFields.CONTENT_MIN_SIZE;
import static manager.utility.StaticFields.KEY_ELEM;
import static manager.utility.StaticFields.KEY_SIZE;
import static manager.utility.StaticFields.OUTBOUND;

import java.nio.ByteBuffer;
import manager.data.RawDataObject;

public class PacketParser {
	
	private static PacketParser instance;

	public RawDataObject create(byte[] b){
		int minSize= KEY_SIZE + CONTENT_MIN_SIZE;
		if(b.length>=minSize){
			RawDataObject rdo = new RawDataObject();

			//recupero la chiave del pacchetto
			byte[] key = new byte[KEY_SIZE];
			for(int i=0;i<KEY_SIZE;i++)
				key[i]=b[i];
			rdo.setKey(key);

			//recupero la dimensione e il contenuto del pacchetto
			if(KEY_ELEM[1]==4){
				ByteBuffer bb = ByteBuffer.allocate(4);
				for(int i=KEY_ELEM[0];i<KEY_ELEM[0]+4;i++)
					bb.put(b[i]);
				bb.flip();
				int size = bb.getInt();
				if(size>0 && b.length>=size){
					System.out.println("dimensione pacchetto: "+size+" pacchetto: "+b.length);
					rdo.setSize(size);
					int contentSize = size-KEY_SIZE;
					byte[] content = new byte[contentSize];
					for(int i=KEY_SIZE;i<size;i++)
						content[i-KEY_SIZE]=b[i];
					rdo.setContent(content);
					return rdo;
				}
			}
		}
		return null;
	}
	
	public RawDataObject create(byte[] key, byte[] content){
		int minSize= KEY_SIZE + CONTENT_MIN_SIZE;
		if((key.length+content.length)>=minSize)
			return new RawDataObject(key, content);
		return null;
	}
	
	
	public boolean parse(RawDataObject rdo){
		if(keyParse(rdo) && contentParse(rdo))
			return true;
		return false;

		
	}
	
	private boolean keyParse(RawDataObject rdo) {
		byte[] b= rdo.getKey();
		int offset=0;
		for(int i=0;i<KEY_ELEM.length;i++){
			byte[] tmp=getFieldKey(b, offset, KEY_ELEM[i]);
			if(tmp!=null){
				switch(i){
				case 0: rdo.setDirection(tmp); break;
				case 1: break;
				case 2: rdo.setTimestamp(tmp); break;
				case 3: rdo.setSeqNumber(tmp); break;	
				case 4: rdo.setSession(tmp); break;
				}
				offset+=KEY_ELEM[i];
			}
			else
				return false;
		}

		return true;
	}
	
	

	private boolean contentParse(RawDataObject rdo) {
		byte[] b = rdo.getContent();
		ByteBuffer bb = ByteBuffer.allocate(4);
		int offset=0;
		/* se mi arriva un pacchetto OUTBOUND 
		 * recupero la KEY del pacchetto INBOUND */
		if(rdo.getDirection()==OUTBOUND){
			byte[] tmp=new byte[KEY_SIZE];
			for(int i=offset;i<KEY_SIZE+offset;i++)
				tmp[i-offset]=b[i];
			rdo.setKeyInbound(tmp);
			offset+=KEY_SIZE;
		}
		for(int i=0;i<CONTENT_ELEM.length;i++){
			byte[] tmp=getFieldContent(b, bb, offset, CONTENT_ELEM[i]);
			if(tmp!=null){
				switch(i){
				case 0: rdo.setUsername(tmp); break;
				case 1: rdo.setPassword(tmp); break;
				case 2: rdo.setOperation(tmp); break;
				case 3: rdo.setWSName(tmp); break;
				case 4: rdo.setWSNamespace(tmp); break;	
				case 5: rdo.setWSDLURL(tmp); break;
				}
				offset+=CONTENT_ELEM[i]+tmp.length;
			}
			else
				return false;
		}
		int lenPar=b.length-offset;
		if(lenPar>0){	
			byte [] tmp=new byte[lenPar];
			for(int i=offset;i<lenPar+offset;i++)
				tmp[i-offset]=b[i];
			rdo.setParams(tmp);
		}
		
		return true;
	}

	
	private byte[] getFieldKey(byte[] b, int offset, int ByteDim) {
		byte[] b2= new byte[ByteDim];
		for(int i=offset;i<offset+ByteDim;i++){
			b2[i-offset]=b[i];
		}
		return b2;
	}
	
	private byte[] getFieldContent(byte[] b, ByteBuffer bb, int offset, int ByteDim){
		int len=0;
		if(ByteDim==4)
			len=integerControl(b, bb,offset);
		if(len>0){
			byte[] b2= new byte[len];
			offset+=ByteDim;
			for(int i=offset;i<len+offset;i++)
				b2[i-offset]=b[i];
			return b2;
		}
		return null;
	}
	
	private int integerControl(byte[] b, ByteBuffer bb, int offset){
		bb.clear();
		for(int i=offset;i<offset+4;i++)
			bb.put(b[i]);
		bb.flip();
		int length = bb.getInt();
		if(length>0)
			return length;
		return 0;
	}
	
	public static synchronized PacketParser getInstance(){
		if(instance==null)
			instance=new PacketParser();
		return instance;
	} 

}
